Rust ya tiene unos cuantos frameworks de interfaz gráfica (Iced, egui, Slint, Dioxus, Leptos), y a finales de septiembre ha aparecido otro: Snow UI. Su versión 0.0.1 se publicó en crates.io el 27 de septiembre de 2026 y el propio README resume el estado del proyecto con una frase sin adornos: «Current status: It works, initially, on desktop and web». Funciona, de momento, en escritorio y en web.
Con esa versión nadie debería montar un producto encima. Aun así, la propuesta de diseño es distinta a la de sus competidores y merece una lectura con calma.
Qué es Snow UI
Es un framework de interfaces escrito en Rust que describe la pantalla con la propia sintaxis del lenguaje, sin HTML, sin JSX y sin un lenguaje de marcado aparte. Lo desarrolla una sola persona, que firma como clia, con licencia Apache-2.0, edición 2024 de Rust y requisito de Rust 1.96. En el repositorio aparece un único producto que lo usa, la web del propio autor (clia.cc).
Las plataformas objetivo son Windows, macOS, Linux, Android, iOS y web. Conviene leer esa lista como una intención y no como una garantía: el README solo da por funcionando escritorio y web, y los móviles siguen sin estar en la frase de estado.
La idea: la interfaz como proyección de un mundo virtual
La mayoría de frameworks modernos repiten que la interfaz es una función del estado. Snow UI parte de otra premisa, que está literalmente en su README: la interfaz es la proyección de un mundo virtual sobre la pantalla. De ahí salen sus piezas básicas:
- World: el mundo completo, raíz de la aplicación.
- Object: cualquier cosa que vive en ese mundo.
- Element: un objeto con atributos, comportamientos y capacidad de intercambiar mensajes (se declara con
#[element]).
El ejemplo con el que abre el README no es un botón ni un formulario, sino una entidad con atributos y acciones diarias. Es una forma poco habitual de presentar un framework de UI, pero deja claro que el modelo de objetos y comportamientos va por delante de los widgets.
use snow_ui::prelude::*;
pub fn world() -> World {
World {
root: obj!(Board {
width: VIEWPORT_WIDTH,
height: VIEWPORT_HEIGHT,
h_align: HAlign::Center,
v_align: VAlign::Middle,
children: list![Card {
children: list![
Row { children: list![Text { text: "Clock Example ?" }] },
Row { children: list![TextClock { format: "%H:%M:%S" }] },
],
}],
}),
..default()
}
}
#[cfg(not(target_arch = "wasm32"))]
fn main() {
snow_ui::launch(world);
}
El árbol de la interfaz son literales de struct dentro de macros (obj!, list!, actions!). Al ser Rust normal, el autocompletado, la navegación al código y los diagnósticos del IDE funcionan sin plugins específicos, algo que no todos los frameworks con DSL propio consiguen.
Estado y mensajes
La comunicación entre componentes se hace con un bus de eventos global (event_bus()) y mensajes tipados con #[message]. El estado vive en State<T>, y cuando cambia, la parte de la interfaz enlazada a él se vuelve a pintar. El ejemplo del contador del README lo muestra completo: un botón envía un mensaje al ser pulsado y otro elemento lo recibe y actualiza su estado.
#[message]
struct IncreaseButtonClicked {}
impl ClickHandler for IncreaseButton {
async fn on_click(&mut self) {
event_bus().send(IncreaseButtonClicked {});
}
}
register_handler!(
impl MessageHandler<IncreaseButtonClicked> for SimpleText {
async fn handle(&mut self, _: &IncreaseButtonClicked, _: &mut MessageContext) {
self.count.update(|c| *c += 1);
}
}
);
Un bus de eventos global es cómodo al principio y problemático cuando los mensajes se multiplican: el orden de los manejadores se vuelve implícito, la lógica se reparte y las cadenas de mensajes esconden el flujo real de datos. Según el propio autor, la disciplina que propone para evitarlo es que los mensajes expresen intención, que el estado sea la única fuente de verdad y que un manejador actualice estado en lugar de lanzar más mensajes.
Asíncrono por diseño
Los manejadores de clic y de mensajes son funciones async, apoyadas en Tokio (con las características time y rt, según el Cargo.toml). Eso permite que temporizadores, peticiones HTTP y refrescos de datos encajen sin hilos ni canales escritos a mano. El ejemplo de login del README combina un Form con un ServerApi que hace un POST con los datos serializados a JSON y, si todo va bien, cambia de pantalla con un Switch.
Qué trae la versión 0.0.1
Tipo | Elementos |
Maquetación | Board, Card, Row |
Controles | Text, Button, TextInput, Switch |
Funciones de más nivel | Form, TextClock, IntervalTimer, ServerApi |
Es un catálogo corto, suficiente para demos, formularios sencillos y pequeñas herramientas, pero lejos de lo que ofrecen los frameworks maduros en tablas, listas virtualizadas, diálogos o accesibilidad avanzada.
Cómo pinta en cada plataforma
Aquí hay un matiz importante que se pasa por alto cuando se resume el proyecto como «Rust puro en todas partes». El Cargo.toml y el README cuentan dos historias distintas:
- Escritorio y móvil: usan Masonry, el motor de widgets del ecosistema Linebender, junto con winit. Snow UI depende de forks propios publicados como
clia_masonryyclia_masonry_winit. - Web: el renderizador de
wasm32no dibuja en un canvas, sino que crea elementos HTML estándar y los actualiza cuando cambia el estado. El autor lo justifica de forma explícita: edición de texto, selección, desplazamiento y accesibilidad ya funcionan en el DOM, y reimplementarlos sería rehacer lo que el navegador ya resuelve.
Es una decisión razonable y distinta a la de otros proyectos que dibujan todo en un canvas. Tiene una consecuencia: en el navegador el código sigue siendo Rust compilado a WebAssembly, pero se apoya en web-sys y wasm-bindgen, que generan el puente con JavaScript habitual en este tipo de proyectos. «Sin JavaScript» significa que el desarrollador no lo escribe, no que el navegador no lo ejecute.
Para quién tiene sentido hoy
Con una versión inicial, un solo mantenedor y unas decenas de descargas en crates.io, el uso razonable es otro que el de producción:
- Estudiar un modelo de objetos, mensajes y estado distinto al de Elm o React.
- Probar demos y herramientas pequeñas en escritorio y navegador.
- Compararlo con Iced, egui, Slint, Dioxus o Leptos para ver qué decisiones de diseño cambian.
Quien necesite API estables, documentación completa y un ecosistema de componentes, por ahora debe mirar a otra parte. Las preguntas que plantea sí son interesantes: si una interfaz es un árbol de pantallas o un mundo de objetos, cómo se concilia un bus de eventos con un flujo de datos unidireccional y si el modelo asíncrono de Rust encaja de forma natural en el frontend.
Enlaces
- Web oficial: snow-ui.dev
- Código en GitHub: github.com/snow-ui-rs/snow-ui
- Paquete en crates.io: crates.io/crates/snow-ui
